package wserver

import (
	"errors"
	"fmt"
	"io"
	"log"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/gorilla/websocket"
)

// Conn wraps websocket.Conn with Conn. It defines to listen and read
// data from Conn.
type Conn struct {
	Conn *websocket.Conn

	AfterReadFunc   func(messageType int, r io.Reader)
	BeforeCloseFunc func()

	once    sync.Once
	id      string
	stopCh  chan int
	body    map[string]string
	qryFlag int
}

//BodyMessage defines messge body struct sent by client
type BodyMessage struct {
	SystemType int `json:"SystemType,string,omitempty"`
}

// Write write p to the websocket connection. The error returned will always
// be nil if success.
func (c *Conn) Write(p []byte) (n int, err error) {
	select {
	case <-c.stopCh:
		return 0, errors.New("Conn is closed, can't be written")
	default:
		fmt.Printf("connId-->%s-->%s\n", c.id, string(p))
		err = c.Conn.WriteMessage(websocket.TextMessage, p)
		if err != nil {
			return 0, err
		}
		return len(p), nil
	}
}

// GetID returns the id generated using UUID algorithm.
func (c *Conn) GetID() string {
	c.once.Do(func() {
		u := uuid.New()
		c.id = u.String()
	})

	return c.id
}

// Listen listens for receive data from websocket connection. It blocks
// until websocket connection is closed.
func (c *Conn) Listen() {
	c.Conn.SetCloseHandler(func(code int, text string) error {
		fmt.Printf("one connection start to close! --code: %d --text: %s \n", code, text)
		if c.BeforeCloseFunc != nil {
			c.BeforeCloseFunc()
		}
		if err := c.Close(); err != nil {
			log.Println(err)
		}

		message := websocket.FormatCloseMessage(code, "closed by client!")
		c.Conn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
		return nil
	})

	// Keeps reading from Conn util get error.
ReadLoop:
	for {
		select {
		case data := <-c.stopCh:
			//	fmt.Printf("--ReadLoop--end--%#v\n", data)
			c.Write([]byte("connect refused, exit code:" + string(data)))
			break ReadLoop
			// TODO  add timer branch to avoid scenoria that there is no message sent out from client
		default:
			fmt.Println("------ReadLoop wait input from client-----")
			messageType, r, err := c.Conn.NextReader() //doubt this is issue
			fmt.Println("--ReadLoop--default-1-", err)
			if err != nil {
				c.Close()
				// TODO: handle read error maybe
				break ReadLoop
			}
			//fmt.Printf("read loop-normal\n")
			if c.AfterReadFunc != nil {
				c.AfterReadFunc(messageType, r)
			}
		}
	}
}

// Close close the connection.
func (c *Conn) Close() error {
	fmt.Println("<--ws conn-close--->")
	//	panic("error vincentzou!")
	select {
	case data := <-c.stopCh:
		fmt.Println("close method stopCH--------", data)
		str := fmt.Sprintf("Conn already have been closed, status is %d \n ", data)
		return errors.New(str)
	default:
		//	fmt.Println("close method default--------")
		c.Conn.Close()
		close(c.stopCh)
		return nil
	}
}

// NewConn wraps conn.
func NewConn(conn *websocket.Conn) *Conn {
	return &Conn{
		Conn:    conn,
		stopCh:  make(chan int, 1),
		qryFlag: 0,
	}
}
